package org.calzz.jajb.io;

/**
 * Default <code>IWriter</code> implementation. The output contains no
 * additional white characters and is ready to be sent e.g. through the network.
 * @see org.calzz.jajb.io.IWriter
 * 
 * @author Marek Brodziak <marek.brodziak@gmail.com>
 * @author Piotr Pejas <piotr.pejas@gmail.com>
 * @since Feb 6, 2008
 */
public class JsonWriter implements IWriter {

	private StringBuilder buffer = new StringBuilder();
	private boolean comma;

	/*
	 * (non-Javadoc)
	 * @see org.calzz.jajb.io.IWriter#startObject(java.lang.String)
	 */
	@Override
	public void startObject(String name) {
		startElement(name, '{');
	}

	/*
	 * (non-Javadoc)
	 * @see org.jamjam.jajb.io.IWriter#endObject()
	 */
	@Override
	public void endObject() {
		endElement('}');
	}

	/*
	 * (non-Javadoc)
	 * @see org.jamjam.jajb.io.IWriter#startArray(java.lang.String)
	 */
	@Override
	public void startArray(String name) {
		startElement(name, '[');
	}

	/*
	 * (non-Javadoc)
	 * @see org.jamjam.jajb.io.IWriter#endArray()
	 */
	@Override
	public void endArray() {
		endElement(']');
	}

	/*
	 * (non-Javadoc)
	 * @see org.jamjam.jajb.io.IWriter#writeKey(java.lang.String)
	 */
	@Override
	public void writeKey(String value) {
		writeComma();
		if (value != null) {
			buffer.append('"').append(value).append('"').append(':');
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.jamjam.jajb.io.IWriter#writeValue(java.lang.String)
	 */
	@Override
	public void writeValue(String value) {
		buffer.append(escape(value));
		comma = true;
	}

	/*
	 * (non-Javadoc)
	 * @see org.calzz.jajb.io.IWriter#writeString(java.lang.String)
	 */
	@Override
	public void writeString(String value) {
		buffer.append('"').append(value).append('"');
		comma = true;
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return buffer.toString();
	}

	/**
	 * Prints a comma if needed.
	 */
	private void writeComma() {
		if (comma) {
			buffer.append(',');
		}
	}

	/**
	 * Starts an object or an array.
	 * @param name name of the object
	 * @param c character which will be printed.
	 */
	private void startElement(String name, char c) {
		writeKey(name);
		buffer.append(c);
		comma = false;
	}

	/**
	 * Ends an element or an array.
	 * @param c character which will be printed.
	 */
	private void endElement(char c) {
		buffer.append(c);
		comma = true;
	}

	/**
	 * Escaped certain values form a given <code>String</code>.
	 * @param value <code>String</code> to be escaped
	 * @return ready-to-use value
	 */
	private String escape(String value) {
		if (value.length() > 3 && value.charAt(0) == '"') {
			return "\"" + value.substring(1, value.length() - 1).replace("\\", "\\\\") // \
					.replace("\"", "\\\"") // "
					.replace("\b", "\\b") // \b
					.replace("\f", "\\f") // \f
					.replace("\n", "\\n") // \n
					.replace("\r", "\\r") // \r
					.replace("\t", "\\t") // \t
					+ "\"";
		}
		return value;
	}
}
